﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace Glucose
    {
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    [Guid("b5fbb2c3-4984-4f18-856a-fb5edbdaa641")]  // NB: Annoyingly duplicated in Setup as well
    public class Functions
        {
        public Functions()
            {
            }

        static double[,] mpMeasuredActualLoessOneTouchGlucoseInH20 = 
        // Known glucose concentrations were measured in H20 on the OneTouch Ultra Mini, then
        // Loess regression was run to fit the data, producing this set of (measured, actual) values.
            {
                {28.0000, 55.7777}, {74.9091, 93.8592}, {121.818, 132.08},  {168.727, 170.551}, 
                {215.636, 212.129}, {262.545, 260.908}, {309.455, 309.973}, {356.364, 363.010}, 
                {403.273, 419.858}, {450.182, 481.034}, {497.091, 542.072}, {544.000, 604.061}
            };

        /// <summary>
        /// Return the calibrated actual glucose concentration that corresponds to a 
        /// measured value as reported by the OneTouch Ultra Mini glucometer.
        /// </summary>
        /// <param name="measuredValue">The concentration reported by the glucometer</param>
        /// <returns></returns>
        public object CalibratedGlucoseMeter(double measuredValue)
            {
            return CalibratedGlucoseMeter(mpMeasuredActualLoessOneTouchGlucoseInH20, measuredValue);
            }

        object CalibratedGlucoseMeter(double[,] mpMeasuredActual, double measuredValue)
            {
            if (measuredValue < mpMeasuredActual[0,0])
                return "Low";
            //
            int iMax = mpMeasuredActual.GetUpperBound(0)+1;
            for (int i=0; i < iMax-1; i++)
                {
                if (mpMeasuredActual[i,0] <= measuredValue && measuredValue < mpMeasuredActual[i+1,0])
                    {
                    double fraction  = (measuredValue -  mpMeasuredActual[i,0]) / (mpMeasuredActual[i+1,0] - mpMeasuredActual[i,0]);
                    double variation = mpMeasuredActual[i+1,1] - mpMeasuredActual[i,1];
                    return mpMeasuredActual[i,1] + fraction * variation;
                    }
                }
            //
            if (measuredValue == mpMeasuredActual[iMax-1,0])
                return mpMeasuredActual[iMax-1,1];
            //
            return "High";
            }

        [ComRegisterFunctionAttribute]
        public static void RegisterFunction(Type type)
            {
            // Register(type);      // we do this with the setup program now
            }

        [ComUnregisterFunctionAttribute]
        public static void UnregisterFunction(Type type)
            {
            // Unregister(type);    // we do this with the setup program now
            }

        private static void Register(Type type)
            {
            System.Diagnostics.Trace.WriteLine("**** registering {0}", type.ToString());
            //
            Registry.ClassesRoot.CreateSubKey(RegistrationSubkey(type, "Programmable"));
            RegistryKey key = Registry.ClassesRoot.OpenSubKey(RegistrationSubkey(type, "InprocServer32"), true);
            key.SetValue("", @"%SystemRoot%\system32\mscoree.dll", RegistryValueKind.ExpandString);
            }

        private static void Unregister(Type type)
            {
            System.Diagnostics.Trace.WriteLine("**** unregistering {0}", type.ToString());
            //
            Registry.ClassesRoot.DeleteSubKey(RegistrationSubkey(type, "Programmable"), false);
            }

        private static string RegistrationSubkey(Type type, string subKeyName)
            {
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            s.Append(@"CLSID\{");
            s.Append(type.GUID.ToString().ToUpper());
            s.Append(@"}\");
            s.Append(subKeyName);
            return s.ToString();
            }
        }
    }
